/**
 * @class 瓦块地图，每个实例都是一个瓦块
 */
class TiledMap {
    /**
     * @constructor
     * @param {Object[]} arr - 对象数组
     * @param {Number} arr[].x - x坐标
     * @param {Number} arr[].y - y坐标
     * @param {Number} arr[].w - 宽
     * @param {Number} arr[].h - 高
     * @param {String} arr[].name - 名字
     * @param {String} arr[].type - 自定义类型 (墙壁 路 障碍建筑 陷阱 可探索建筑/宝箱/npc等,门)
     */
    constructor(arr) {
        this.tiledMap = {};
        this._init(arr);
    }

    /**
     * 初始化
     */
    _init(arr) {
        arr.forEach(el => {
            this.tiledMap[el.name] = el;
        })
    }
}

/**
 * @class 地图 用于生成地图对象和绘制地图
 */
class Maps extends Base {
    /**
     * @constructor
     * @param {String} name - 名字
     * @param {JSON} imgObj - 拼接的瓦块图或未拼接的瓦块图对象({name: Image}[,{...}])
     * @param {Number} w - 地图总宽度
     * @param {Number} h - 地图总高度
     * @param {Number} cols - 地图宽度总列数
     * @param {Number} rows - 地图高度总行数
     * @param {Number} cellw - 每个图块在地图中所占宽度
     * @param {Number} cellh - 每个图块在地图中所占高度
     * @param {Object} painter - 可选参数:绘制方法对象
     * @param {Function} painter.paint(sprite, ctx) - 绘制方法
     */
    constructor(name, imgObj, w, h, cols, rows, cellw, cellh, painter = undefined) {
        super(painter);
        this.name = name;
        this.w = w;
        this.h = h;
        this.cols = cols;
        this.rows = rows;
        this.imgObj = imgObj;
        this.cellw = cellw;
        this.cellh = cellh;
        this._roadnames = [];
        this._explorenames = [];
        this._wallnames = [];
        /**
         * 包含瓦块地图数据的全地图数组
         */
        this._mapRes = [];
        this._roads = []; // 所有需要铺设道路包含墙体的坐标;
        this._walls = []; // 所有墙体坐标;
    }

    /**
     * 随机生成地图
     * 地图类型：图块为方形地块无墙(默认)
     * @param {TiledMap} tiledMap - 包含瓦片实例的对象实例
     * @param {Array | String} roadnames - 瓦片实例中道路名字数组,或者名字字符串
     * @param {Array | String} wallnames - 瓦片实例中墙体名字数组,或者名字字符串
     * @example 允许链式调用
     */
    randomGeneration(tiledMap, roadnames = [], wallnames = []) {
        var tiledMap = tiledMap.tiledMap;
        var roadnames = typeof roadnames == 'string' ? [roadnames] : roadnames;
        var wallnames = typeof wallnames == 'string' ? [wallnames] : wallnames;
        [this._roadnames, this._wallnames] = [roadnames, wallnames];
        /**生成的地图二维数组 */
        let mapArr = [];
        /**a星算法实例 */
        let astart = new Astart(this.rows, this.cols);
        let rooms = [];
        /**初始化地图 */
        let init = () => {
            for (let y = 0; y < this.rows; y++) {
                mapArr[y] = [];
                for (let x = 0; x < this.cols; x++) {
                    if (Math.random() > 0.96) {
                        rooms.push({ w: Math.floor(Math.random() * (this.cols / 2 - 2) + 2), h: Math.floor(Math.random() * (this.rows / 2 - 2) + 2) });
                    }
                    mapArr[y][x] = false;
                }
            }
        }

        /**随机生成地块并联通 */
        let randomGenerationRoom = () => {
            /**将房间随机散步在地图外切圆内，并筛选合格的 */
            let rankRooms = () => {
                let tmp = [];
                let center = {x: Math.floor(this.cols / 2), y: Math.floor(this.rows / 2), w: 1, h: 1};
                let r = Math.floor(Math.sqrt(Math.pow(this.cols , 2) + Math.pow(this.rows, 2)) / 2);
                for (let i = 0; i < rooms.length; i++) {
                    let [x, y] = getRandomPointInCircle(r, 1);
                    x = center.x + x < 0 ? 0 : center.x + x;
                    y = center.y + y < 0 ? 0 : center.y + y;
                    if (x >= this.cols || y >= this.rows) {
                        rooms.splice(i, 1);
                        continue;
                    }
                    if (x + rooms[i].w >= this.cols) {
                        rooms[i].w = this.cols - x - 1;
                    }
                    if (y + rooms[i].h >= this.rows) {
                        rooms[i].h = this.rows - y - 1;
                    }
                    tmp[i] = {...rooms[i], x, y};
                }
                rooms = tmp.filter(el => el);
            }
            
            /**将房间对应到地图上 */
            let roomsInmap = () => {
                for(let index = 0; index < rooms.length; index++) {
                    let el = rooms[index];
                    for (let i = 0; i < el.h; i++) {
                        for (let j = 0; j < el.w; j++) {
                            mapArr[el.y + i][el.x + j] = 'road';
                        }
                    }
                }
            }

            rankRooms();
            roomsInmap();
        }

        /**获取周围8个点 */
        let getAround = obj => {
            let around = [];
            let rows = this.rows;
            let cols = this.cols;
            let y = obj.y;
            let x = obj.x;
            if (y - 1 >= 0) {
                around.push(mapArr[y - 1][x]);
            }
            if (x - 1 >= 0) {
                around.push(mapArr[y][x - 1]);
            }
            if (x + 1 < cols) {
                around.push(mapArr[y][x + 1]);
            }
            if (y + 1 < rows) {
                around.push(mapArr[y + 1][x]);
            }
            if (x - 1 >= 0 && y - 1 >= 0) {
                around.push(mapArr[y - 1][x - 1]);
            }
            if (x + 1 < cols && y - 1 >= 0) {
                around.push(mapArr[y - 1][x + 1]);
            }
            if (x - 1 >= 0 && y + 1 < rows) {
                around.push(mapArr[y + 1][x - 1]);
            }
            if (x + 1 < cols && y + 1 < rows) {
                around.push(mapArr[y + 1][x + 1]);
            }
            return around;
        }

        /**设置通路 */
        let paving = () => {
            astart.setMode(false); // 寻路模式
            let getPath = spot => {
                if(!spot.parent) {
                    return true;
                }
                try {
                    mapArr[spot.y][spot.x] = 'road';
                    getPath(spot.parent);
                } catch(e) {
                    return false;
                }
            }

            for (let i = 0; i < mapArr.length; i++) {
                for (let j = 0; j < mapArr[i].length; j++) {
                    if (mapArr[i][j]) {
                        astart.grids[i][j].isGo = false;
                    } else {
                        astart.grids[i][j].isGo = true;
                    }
                }
            }

            for (let i = 0; i < rooms.length; i++) {
                // let s1 = rooms[i];
                for(let j = 1; j < rooms.length; j++) {
                    let s1 = rooms[i];
                    let s2 = rooms[j];
                    let spot = astart.selectSpot(s1, s2);
                    getPath(spot);
                }
                // let diffs = rooms.map((el, index) => ({diff: Math.abs(s1.x - el.x) + Math.abs(s1.y - el.y), index}));
                // diffs.splice(i, 1);
                // let diffmin = Math.min(...diffs.map(el => el.diff));
                // let indexs = diffs.filter(el => el.diff == diffmin);
                // for (let i = 0; i < indexs.length; i++) {
                //     let s2 = rooms[i];
                //     let spot = astart.selectSpot(s1, s2);
                //     getPath(spot);
                // }                
            }
        }

        /**优化maparr */
        let optiwall = () => {
            for (let i = 0; i < mapArr.length; i++) {
                for (let j = 0; j < mapArr[i].length; j++) {
                    let wall = getAround({ x: j, y: i }).filter(curr => { return !curr }).length;
                    if(wall > 5) {
                        mapArr[i][j] = false;
                    }
                }
            }
        }

        /**mapArr中元素和瓦片实例对应 */
        let map2tiled = () => {
            for (let i = 0; i < mapArr.length; i++) {
                for (let j = 0; j < mapArr[i].length; j++) {
                    let tmp = mapArr[i][j];
                    if (!tmp) {
                        continue;
                    }
                    if (['road', 'door', 'explore'].includes(tmp)) {
                        let key = roadnames[Math.floor(Math.random() * roadnames.length)];
                        mapArr[i][j] = tiledMap[key];
                    } else {
                        this._walls.push({ y: i, x: j });
                        mapArr[i][j] = tiledMap[tmp];
                    }
                    this._roads.push({ y: i, x: j });
                }
            }
        }

        init();
        randomGenerationRoom();
        paving();
        map2tiled();

        this._mapRes = mapArr;
        return this;
    }

    /**
     * 绘制背景图片或者纯色背景
     * @param {Object} ctx - 绘画上下文
     * @param {Image | String} img - 背景图块/背景图 或者颜色
     * @param {Number} x - x坐标
     * @param {Number} y - y坐标
     * @param {Number} w 大小
     * @param {Number} h 大小
     * @example 允许链式调用
     */
    drawBg(ctx, img, x, y, w, h) {
        this.painter = {};
        if (img instanceof Image) {
            if (w == this.w && h == this.h) {
                this.painter.paint = (sprite, ctx) => {
                    ctx.drawImage(img, x, y, w, h, 0, 0, w, h);
                }
            } else {
                this.painter.paint = (sprite, ctx) => {
                    let col = Math.ceil(sprite.w / w);
                    let row = Math.ceil(sprite.h / h);
                    for (let i = 0; i < row; i++) {
                        for (let j = 0; j < col; j++) {
                            ctx.drawImage(img, x, y, w, h, j * w, i * h, w, h);
                        }
                    }
                }
            }
        } else {
            this.painter.paint = (sprite, ctx) => {
                ctx.save();
                ctx.fillStyle = img;
                ctx.fillRect(0, 0, sprite.w, sprite.h);
                ctx.restore();
            }
        }
        this.paint(ctx);
        return this;
    }

    /**
     * 绘制地图 地层和墙体
     * @param {Object} ctx - canvas的上下文环境
     * @param {Object} diff - 图块间距离
     * @example 允许链式调用
     */
    draw(ctx, diff = { w: 0, h: 0 }) {
        let pr = (sprite, ctx, index) => {
            let obj = sprite._roads[index];
            let el = sprite._mapRes[obj.y][obj.x];
            if (el) {
                let dx = obj.x * (sprite.cellw - diff.w) + (sprite.w - sprite.cols * (sprite.cellw - diff.w)) / 2;
                let dy = obj.y * (sprite.cellh - diff.h) + (sprite.h - sprite.rows * (sprite.cellh - diff.h)) / 2;
                let picture = sprite.imgObj[el.name] ? sprite.imgObj[el.name] : sprite.imgObj[el.type];
                ctx.drawImage(picture, el.x, el.y, el.w, el.h, dx, dy, sprite.cellw, sprite.cellh);
            }
        }
        this.painter = {
            paint(sprite, ctx) {
                for (let i = 0; i < sprite._roads.length; i++) {
                    pr(sprite, ctx, i);
                }
                for (let j = 0; j < sprite._walls.length; j++) {
                    pr(sprite, ctx, j);
                }
            }
        }
        this.paint(ctx);
        return this;
    }

    /**
     * 获取地图数据数组
     * @return {Array} 包含瓦块地图数据的全地图数组
     */
    get map() {
        return this._mapRes;
    }
}